package com.supplychain.scm.core.delay.config;

import jodd.util.StringPool;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Objects;

/**
 * @author liyang
 * @version 1.0.0
 * @ClassName RedissonAutoConfiguration
 * @createTime 17:17:00 2022/10/14
 */
@Configuration
@ConditionalOnClass({RedissonClient.class, Redisson.class})
@EnableConfigurationProperties(RedissonProperties.class)
@AutoConfigureOrder(1000)
@ConditionalOnProperty(prefix = RedissonProperties.PREFIX + StringPool.DOT + "delay-redisson", value = "enabled", havingValue = "true")
public class RedissonAutoConfiguration {

    @Scope(BeanDefinition.SCOPE_SINGLETON)
    @Bean(name = RedissonConfigUtils.REDISSON_QUEUE_BEAN_PROCESSOR_BEAN_NAME)
    public RedissonQueueBeanPostProcessor redissonQueueBeanPostProcessor() {
        return new RedissonQueueBeanPostProcessor();
    }

    @Scope(BeanDefinition.SCOPE_SINGLETON)
    @Bean(name = RedissonConfigUtils.REDISSON_QUEUE_REGISTRY_BEAN_NAME)
    public RedissonQueueRegistry redissonQueueRegistry() {
        return new RedissonQueueRegistry();
    }

    @Scope(BeanDefinition.SCOPE_SINGLETON)
    @Bean
    @ConditionalOnMissingBean
    public RedissonTemplate redissonTemplate() {
        return new RedissonTemplate();
    }

    @Scope(BeanDefinition.SCOPE_SINGLETON)
    @Bean(destroyMethod = "shutdown")
    @ConditionalOnMissingBean({RedissonClient.class})
    public RedissonClient redissonClient(RedissonProperties properties) {
        RedissonProperties.Redisson redisson = properties.getDelayRedisson();
        boolean isCluster = (Objects.nonNull(properties.getCluster()) && !CollectionUtils.isEmpty(properties.getCluster().getNodes())) ? Boolean.TRUE : Boolean.FALSE;
        Assert.notNull(redisson, "redisson config non-existent");
        //final ServerType serverType = properties.getServerType();
        //final String serverAddress = properties.getServerAddress();
        //Assert.hasText(serverAddress, "redis cluster nodes config error");
        final String password = properties.getPassword();
        Config config = new Config();
        config.setThreads(redisson.getThreads());
        config.setNettyThreads(redisson.getNettyThreads());
        config.setLockWatchdogTimeout(redisson.getLockWatchdogTimeoutMillis());
        if (!isCluster) {
            //redis单机配置
            SingleServerConfig singleServerConfig = config.useSingleServer();
            //private String serverAddress = "redis://localhost:6379";
            singleServerConfig.setAddress(StringUtils.hasText(properties.getUrl()) ? this.checkAndFixAddress(properties.getUrl()) : this.setAddress(properties));
            if (StringUtils.hasText(password)) {
                singleServerConfig.setPassword(password);
            }
            singleServerConfig.setDatabase(properties.getDatabase());
            singleServerConfig.setConnectTimeout(redisson.getConnectTimeoutMillis());
            singleServerConfig.setTimeout(redisson.getSocketTimeoutMillis());
            singleServerConfig.setKeepAlive(redisson.isKeepAlive());
            singleServerConfig.setRetryAttempts(redisson.getRetryAttempts());
            singleServerConfig.setRetryInterval(redisson.getRetryIntervalMillis());
            singleServerConfig.setConnectionPoolSize(redisson.getMaxPoolSize());
            singleServerConfig.setConnectionMinimumIdleSize(redisson.getMinIdleSize());
            singleServerConfig.setIdleConnectionTimeout(redisson.getMaxIdleMillis());
            return Redisson.create(config);
        }
        if (isCluster) {
            //redis 集群配置
            ClusterServersConfig clusterServersConfig = config.useClusterServers();
            RedissonProperties.Cluster cluster = properties.getCluster();
            List<String> nodes = cluster.getNodes();
            for (String node : nodes) {
                clusterServersConfig.addNodeAddress(this.checkAndFixAddress(node));
            }
            if (StringUtils.hasText(password)) {
                clusterServersConfig.setPassword(password);
            }
            clusterServersConfig.setConnectTimeout(redisson.getConnectTimeoutMillis());
            clusterServersConfig.setTimeout(redisson.getSocketTimeoutMillis());
            clusterServersConfig.setKeepAlive(redisson.isKeepAlive());
            clusterServersConfig.setRetryAttempts(redisson.getRetryAttempts());
            clusterServersConfig.setRetryInterval(redisson.getRetryIntervalMillis());
            final int masterMaxPoolSize = Objects.nonNull(redisson.getMaster()) ? redisson.getMaster().getMaxPoolSize() : redisson.getMaxPoolSize();
            final int masterMinIdleSize = Objects.nonNull(redisson.getMaster()) ? redisson.getMaster().getMinIdleSize() : redisson.getMinIdleSize();
            final int slaveMaxPoolSize = Objects.nonNull(redisson.getSlave()) ? redisson.getSlave().getMaxPoolSize() : redisson.getMaxPoolSize();
            final int slaveMinIdleSize = Objects.nonNull(redisson.getSlave()) ? redisson.getSlave().getMinIdleSize() : redisson.getMinIdleSize();
            clusterServersConfig.setMasterConnectionPoolSize(masterMaxPoolSize);
            clusterServersConfig.setMasterConnectionMinimumIdleSize(masterMinIdleSize);
            clusterServersConfig.setSlaveConnectionPoolSize(slaveMaxPoolSize);
            clusterServersConfig.setSlaveConnectionMinimumIdleSize(slaveMinIdleSize);
            clusterServersConfig.setIdleConnectionTimeout(redisson.getMaxIdleMillis());
            return Redisson.create(config);
        }
        throw new BeanCreationException("redis server type is illegal");
    }

    private String checkAndFixAddress(String address) {
        final String protocol = "redis://";
        final String sslProtocol = "rediss://";
        if (address.startsWith(protocol) || address.startsWith(sslProtocol)) {
            return address;
        }
        return protocol + address;
    }

    private String setAddress(RedissonProperties properties) {
        final String protocol = "redis://";
        final String sslProtocol = "rediss://";
        if (properties.isSsl()) {
            return sslProtocol.concat(properties.getHost()).concat(StringPool.COLON).concat(String.valueOf(properties.getPort()));
        } else {
            return protocol.concat(properties.getHost()).concat(StringPool.COLON).concat(String.valueOf(properties.getPort()));
        }
    }
}
